package paim.wingchun.model.validadores;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Level;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import paim.wingchun.app.WC_Log;
import paim.wingchun.app.WC_Strings;
import paim.wingchun.app.WC_Utils;
import paim.wingchun.app.Constantes;
import paim.wingchun.app.Reflections;
import paim.wingchun.model.AField;
import paim.wingchun.model.Validacao;
import paim.wingchun.model.modelos.ErroModel;
import paim.wingchun.model.pojos.Erro;
import paim.wingchun.model.pojos.Pojo;

public class Validador {

    /**
     * Verifica se atributo e nao-nulo. <BR>
     * <BR>
     * Obs.: Uma String vazia e considerado nulo <BR>
     * Obs2.: Um valor {@link BigDecimal#ZERO} e considerado nulo <BR>
     * Obs3.: Um lista vazia considerado nulo
     * 
     * @author paim 13/05/2011
     * @param <P>
     * @param pojo
     * @return List<Erro>
     */
    public static <P extends Pojo> List<Erro> naoNulo(Pojo pojo) {
        List<Erro> erros = new ArrayList<Erro>();

        List<Field> fields = Reflections.getDeepFields((Class<P>) Reflections.getClasse(pojo));
        for ( Field field : fields ) {

            /* anotacao de validacao para o field */
            Validacao validacao = field.getAnnotation(Validacao.class);

            /* se nao foi especificado, entao sera Level.NENHUM */
            ErroModel.Level level = (validacao == null ? ErroModel.Level.NENHUM : validacao.nulo());

            if ( ErroModel.Level.NENHUM.equals(level) )
                continue;

            AField aField = field.getAnnotation(AField.class);
            Object valor;
            try {
                valor = Reflections.getValorField(pojo, field);
            }
            catch ( Exception e ) {
                WC_Log.getInstancia().getlogger().log(Level.SEVERE, e.getMessage(), e);
                valor = null;
            }

            if ( (valor == null) || (valor instanceof String && WC_Strings.isEmpty((String) valor))
                    || (valor instanceof BigDecimal && ((BigDecimal) valor).compareTo(BigDecimal.ZERO) == 0)
                    || (valor instanceof Collection && ((Collection<?>) valor).isEmpty()) ) {

                Erro erro = new Erro(1L, level, pojo, field.getName(), new String[] { aField.rotulo() });
                erros.add(erro);
            }
        }
        return erros;
    }

    /**
     * verifica se eh negativo<BR>
     * Obs. verificando somente Long e Integer
     * 
     * @author paim 13/05/2011
     * @param pojo
     * @return List<Erro>
     */
    public static List<Erro> naoNegativo(Pojo pojo) {
        List<Erro> erros = new ArrayList<Erro>();

        List<Field> fields = Reflections.getDeepFields((Class<? extends Pojo>) Reflections.getClasse(pojo));
        for ( Field field : fields ) {

            /* anotacao de validacao para o field */
            Validacao validacao = field.getAnnotation(Validacao.class);

            /* se nao foi especificado, entao sera Level.NENHUM */
            ErroModel.Level level = (validacao == null ? ErroModel.Level.NENHUM : validacao.negativo());

            if ( ErroModel.Level.NENHUM.equals(level) )
                continue;

            /* somente numeros */
            if ( !Number.class.isAssignableFrom(field.getType()) )
                continue;

            Object valor;
            try {
                valor = Reflections.getValorField(pojo, field);
            }
            catch ( Exception e ) {
                WC_Log.getInstancia().getlogger().log(Level.SEVERE, e.getMessage(), e);
                valor = null;
            }

            /* Valida negativo para Integer e Long */
            if ( ((field.getType().equals(Integer.class)) && ((Integer) valor < 0))
                    || ((field.getType().equals(Long.class) && ((Long) valor < 0))) )
                // FIXME erroid
                erros.add(new Erro(2L, ErroModel.Level.ERRO$IMPEDITIVO, ErroModel.Tipo.VALIDACAO, pojo, field.getName()));
        }
        return erros;
    }
    
    public static List<Erro> naoZero(Pojo pojo) {
        List<Erro> erros = new ArrayList<Erro>();
        //FIXME implemente-me, e troque o nome dos outros
        return erros;
    }

    /**
     * @author paim 13/05/2011
     * @param pojo
     * @return List<Erro>
     */
    public static List<Erro> validaData(Pojo pojo) {
        List<Erro> erros = new ArrayList<Erro>();

        List<Field> fields = Reflections.getDeepFields((Class<? extends Pojo>) Reflections.getClasse(pojo));
        for ( Field field : fields ) {

            /* anotacao de validacao para o field */
            Validacao validacao = field.getAnnotation(Validacao.class);

            /* se nao foi especificado, entao sera Level.NENHUM */
            ErroModel.Level level = (validacao == null ? ErroModel.Level.NENHUM : validacao.negativo());

            if ( ErroModel.Level.NENHUM.equals(level) )
                continue;

            /* somente datas */
            if ( !java.sql.Date.class.isAssignableFrom(field.getType()) )
                continue;

            Object valor;
            try {
                valor = Reflections.getValorField(pojo, field);
            }
            catch ( Exception e ) {
                WC_Log.getInstancia().getlogger().log(Level.SEVERE, e.getMessage(), e);
                valor = null;
            }

            if ( valor instanceof Date && ((Date) valor).getTime() == Constantes.DATA_INVALIDA )
                // FIXME erroid
                erros.add(new Erro(4L, ErroModel.Level.ERRO$PRIORITARIO, ErroModel.Tipo.VALIDACAO, ((Pojo) pojo),
                        field.getName()));

        }
        return erros;
    }

    /**
     * @author paim 13/05/2011
     * @param pojo
     * @return List<Erro>
     */
    public static List<Erro> validaCampo(Pojo pojo) {
        List<Erro> erros = new ArrayList<Erro>();

        List<Field> fields = Reflections.getDeepFields((Class<? extends Pojo>) Reflections.getClasse(pojo));
        for ( Field field : fields ) {

            /* anotacao de validacao para o field */
            Validacao validacao = field.getAnnotation(Validacao.class);

            /* se nao foi especificado, entao sera Level.NENHUM */
            ErroModel.Level level = (validacao == null ? ErroModel.Level.NENHUM : validacao.campo());

            if ( ErroModel.Level.NENHUM.equals(level) )
                continue;

            WC_Utils.Campo campo = (validacao == null ? WC_Utils.Campo.NENHUM : validacao.tipoCampo());

            if ( WC_Utils.Campo.NENHUM.equals(campo) )
                continue;

            if ( !String.class.isAssignableFrom(field.getType()) )
                continue;

            Object valor;
            try {
                valor = Reflections.getValorField(pojo, field);
            }
            catch ( Exception e ) {
                WC_Log.getInstancia().getlogger().log(Level.SEVERE, e.getMessage(), e);
                valor = null;
            }
            if ( valor == null || valor.equals("") )
                continue;

            if ( WC_Utils.Campo.EMAIL.equals(campo) ) {
                if ( !(Validador.validarEmail(valor.toString())) )
                    // FIXME erroid
                    erros.add(new Erro(33L, level, ErroModel.Tipo.VALIDACAO, pojo, field.getName()));
            }
        }
        return erros;
    }

    public static boolean validarEmail(String email) {
        email = WC_Strings.removeAcentos(email.replaceAll("ï¿½", "c"));
        Pattern padrao = Pattern.compile("[a-zA-Z0-9_-]+[\\.[a-zA-Z0-9]+]*@[a-zA-Z0-9_-]+[\\.[a-zA-Z]+]+");
        Matcher pesquisa = padrao.matcher(email);
        if ( pesquisa.matches() )
            return true;
        else
            return false;
    }
}
